<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>关系图</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas">您的浏览器不支持 canvas</canvas>
</body>
<script>
  const canvas = document.getElementById('canvas')
  const ctx = canvas.getContext('2d')

  canvas.style.width = document.documentElement.clientWidth - 6 + 'px'
  canvas.style.height = document.documentElement.clientHeight - 6 + 'px'
  canvas.width = document.documentElement.clientWidth * 1.5
  canvas.height = document.documentElement.clientHeight * 1.5

  class Ball {
    constructor(options) {
      this.canvas = options.canvas
      this.text = options.title
      this.ctx = this.canvas.getContext('2d')
      this.wd = this.canvas.clientWidth * 1.5
      this.ht = this.canvas.clientHeight * 1.5
      this.r = Math.random() * 40 + 10
      this.x = Math.random() * (this.wd - (this.r * 2)) + this.r
      this.y = Math.random() * (this.ht - (this.r * 2)) + this.r
      this.color = '#' + parseInt(Math.random() * 0xFFFFFF).toString(16)
      this.xSpeed = Math.random() * 4 + 6
      this.ySpeed = Math.random() * 6 + 4
      this.init()
    }

    init() {
      this.run()
      this.draw()
    }

    draw() {
      this.drawCircle()
      this.drawText(this.text, this.x, this.y + this.r + 10)
    }

    // 绘制圆形
    drawCircle() {
      this.ctx.beginPath()
      this.ctx.fillStyle = this.color
      this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
      this.ctx.fill()
      this.ctx.closePath()
    }

    // 绘制文字
    drawText(text, x, y) {
      this.ctx.font = 'normal 20px 微软雅黑'
      this.ctx.textAlign = 'center'
      this.ctx.textBaseline = 'middle'
      this.ctx.fillText(text, x, y)
    }

    // 绘制线条
    drawLine(x1, y1, x2, y2, color) {
      this.ctx.beginPath()
      this.ctx.lineWidth = 1
      this.ctx.strokeStyle = color || '#666'
      this.ctx.moveTo(x1, y1)
      this.ctx.lineTo(x2, y2)
      this.ctx.stroke()
      this.ctx.closePath()
    }

    run() {
      if (this.x - this.r <= 0 || this.x + this.r >= this.wd) {
        this.xSpeed = -this.xSpeed
      }
      if (this.y - this.r <= 0 || this.y + this.r >= this.ht) {
        this.ySpeed = -this.ySpeed
      }
      this.x += this.xSpeed
      this.y += this.ySpeed
    }
  }

  let ballArr = []
  let titleArr = ['Vue', 'Webpack', 'React', 'Angular', 'Python', 'Nodejs', 'eCharts', 'Next', '模块化', 'Bootstrap', 'Electron', 'flutter', '小程序', '混合应用', 'Git',]

  for (let i = 0; i < 8; i++) {
    let ball = new Ball({
      canvas: canvas,
      title: titleArr[i]
    })
    ballArr.push(ball)


    // 连线
    for (let j = 0; j < i; j++) {
      let preBall = ballArr[j]
      ball.drawLine(ball.x, ball.y, preBall.x, preBall.y)
    }
  }


  // 做动画
  setInterval(() => {
    ctx.clearRect(0, 0, canvas.clientWidth * 1.5 + 10, canvas.clientHeight * 1.5 + 10)
    for (let i = 0; i < ballArr.length; i++) {
      let ball = ballArr[i]
      // 连线
      for (let j = 0; j < i; j++) {
        let preBall = ballArr[j]
        ball.drawLine(ball.x, ball.y, preBall.x, preBall.y, ball.color)
      }
    }
    for (let i = 0; i < ballArr.length; i++) {
      let ball = ballArr[i]
      ball.init()
    }
  }, 15)

</script>

</html>